/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright(c) 2020 - 2023 Allwinner Technology Co.,Ltd. All rights reserved. */
/*
 * Copyright (C) 2016 Allwinner.
 * fuzhaoke <fuzhaoke@allwinnertech.com>
 *
 * SUNXI SCR Controller Driver
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/major.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <asm/irq.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/version.h>
#include "sunxi-scr.h"

/* ====================  For debug  =============================== */
#define SCR_ENTER()    pr_info("%s()%d - %s\n", __func__, __LINE__, "Enter ...")
#define SCR_EXIT()	    pr_info("%s()%d - %s\n", __func__, __LINE__, "Exit")
#define SCR_DBG(fmt, arg...) pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SCR_INFO(fmt, arg...) pr_info("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SCR_WARN(fmt, arg...) pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SCR_ERR(fmt, arg...)   pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg)

static struct sunxi_scr *pscr;
static int sunxi_scr_major;
static struct class *scr_dev_class;
static struct device *scr_device;

struct scr_data {
	uint8_t buf[SCR_BUF_SIZE];
	uint16_t cnt;  /* valid count of data */
};

static struct scr_data scr_buf_rx, scr_buf_tx;

static void sunxi_scr_do_atr(struct sunxi_scr *pscr);

/* =================  smart card reader basic interface  =================== */
/* clear control and status register */
static inline void scr_clear_csr_reg(void __iomem *reg_base)
{
	writel(0x0, reg_base + SCR_CSR_OFF);
}

/* get control and status register config */
static inline uint32_t scr_get_csr_config(void __iomem *reg_base)
{
	return readl(reg_base + SCR_CSR_OFF);
}

/* get detect status 0:card remove, 1:card insert */
static inline uint8_t scr_get_det_status(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);

	return (reg_val >> 31) & 0x1;
}

/* set detect polarity, 0:low active, 1:high active */
static inline void scr_set_det_polar(void __iomem *reg_base, uint8_t config)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 24);
	reg_val |= ((config & 0x1) << 24);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* set protocol 0:T0, 1:T1, 2~3:reserved */
static inline void scr_set_t_protocol(void __iomem *reg_base, uint8_t config)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x3 << 22);
	reg_val |= ((config & 0x3) << 22);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* when enable(1), both RX&TX FIFO will be flush before ATR start */
static inline void scr_set_atr_flush(void __iomem *reg_base, uint8_t config)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 21);
	reg_val |= ((config & 0x1) << 21);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* when enable(1), TS character(first ATR character) will be stored in FIFO */
static inline void scr_set_ts_recv(void __iomem *reg_base, uint8_t config)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 20);
	reg_val |= ((config & 0x1) << 20);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* scclk output state during clock stop, 0:low level, 1:high level */
static inline void scr_set_clk_polar(void __iomem *reg_base, uint8_t config)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 19);
	reg_val |= ((config & 0x1) << 19);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* parity error character receive enable,
 * 0:disable, 1:enable store error parity in RX FIFO
 */
static inline void scr_set_recv_parity(void __iomem *reg_base, uint8_t config)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 18);
	reg_val |= ((config & 0x1) << 18);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

static inline uint8_t scr_get_recv_parity(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);

	return (reg_val >> 18) & 0x1;
}

/* 0:lsb first, 1:msb first */
static inline void scr_set_data_order(void __iomem *reg_base, uint8_t config)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 17);
	reg_val |= ((config & 0x1) << 17);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* when enable(1), invert data level */
static inline void scr_set_data_invert(void __iomem *reg_base, uint8_t config)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 16);
	reg_val |= ((config & 0x1) << 16);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* deinit card after stop use and auto clear when finish */
static inline void scr_set_deactivation(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val |= (0x1 << 11);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* init card before start use and auto clear when finish */
static inline void scr_set_activation(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val |= (0x1 << 10);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* reset card */
static inline void scr_set_warmreset(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val |= (0x1 << 9);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

static inline void scr_set_clk_stop(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val |= (0x1 << 8);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

static inline void scr_set_clk_restart(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 8);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

static inline void scr_global_interrupt_enable(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val |= (0x1 << 2);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

static inline void scr_global_interrupt_disable(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 2);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* enable receive data stored in RX FIFO */
static inline void scr_receive_enable(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val |= (0x1 << 1);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* disable receive data stored in RX FIFO */
static inline void scr_receive_disable(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 1);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* enable transmit data stored in TX FIFO */
static inline void scr_transmit_enable(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val |= (0x1 << 0);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* disable transmit data stored in TX FIFO */
static inline void scr_transmit_disable(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CSR_OFF);
	reg_val &= ~(0x1 << 0);
	writel(reg_val, reg_base + SCR_CSR_OFF);
}

/* set each interrupt bit mask */
static inline void scr_set_interrupt_enable(void __iomem *reg_base, uint32_t bm)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_INTEN_OFF);
	reg_val |= bm;
	writel(reg_val, reg_base + SCR_INTEN_OFF);
}

/* disable interrupt bit mask */
static inline void scr_set_interrupt_disable(void __iomem *reg_base,
					     uint32_t bm)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_INTEN_OFF);
	reg_val &= ~bm;
	writel(reg_val, reg_base + SCR_INTEN_OFF);
}

/* get all interrupt status */
static inline uint32_t scr_get_interrupt_status(void __iomem *reg_base)
{
	return readl(reg_base + SCR_INTST_OFF);
}

/* write 1 to clear interrupt flag */
static inline void scr_clear_interrupt_status(void __iomem *reg_base,
					      uint32_t bm)
{
	writel(bm, reg_base + SCR_INTST_OFF);
}

/* flush RX FIFO and auto clear when finish */
static inline void scr_flush_rxfifo(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_FCSR_OFF);
	reg_val |= (0x1 << 10);
	writel(reg_val, reg_base + SCR_FCSR_OFF);
}

static inline bool scr_rxfifo_is_full(void __iomem *reg_base)
{
	return (readl(reg_base + SCR_FCSR_OFF) >> 9) & 0x1;
}

static inline bool scr_rxfifo_is_empty(void __iomem *reg_base)
{
	return (readl(reg_base + SCR_FCSR_OFF) >> 8) & 0x1;
}

/* flush TX FIFO and auto clear when finish */
static inline void scr_flush_txfifo(void __iomem *reg_base)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_FCSR_OFF);
	reg_val |= (0x1 << 2);
	writel(reg_val, reg_base + SCR_FCSR_OFF);
}

static inline bool scr_txfifo_is_full(void __iomem *reg_base)
{
	return (readl(reg_base + SCR_FCSR_OFF) >> 1) & 0x1;
}

static inline bool scr_txfifo_is_empty(void __iomem *reg_base)
{
	return readl(reg_base + SCR_FCSR_OFF) & 0x1;
}

static inline void scr_set_rxfifo_threshold(void __iomem *reg_base, uint8_t thh)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_FCNT_OFF);
	reg_val &= ~(0xffU << 24);
	reg_val |= thh << 24;
	writel(reg_val, reg_base + SCR_FCNT_OFF);
}

static inline void scr_set_txfifo_threshold(void __iomem *reg_base, uint8_t thh)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_FCNT_OFF);
	reg_val &= ~(0xffU << 16);
	reg_val |= thh << 16;
	writel(reg_val, reg_base + SCR_FCNT_OFF);
}

static inline uint8_t scr_get_rxfifo_count(void __iomem *reg_base)
{
	return (readl(reg_base + SCR_FCNT_OFF) >> 8) & 0xff;
}

static inline uint8_t scr_get_txfifo_count(void __iomem *reg_base)
{
	return readl(reg_base + SCR_FCNT_OFF) & 0xff;
}

static inline void scr_set_rx_repeat(void __iomem *reg_base, uint8_t repeat)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_RPT_OFF);
	reg_val &= ~(0xf << 4);
	reg_val |= (repeat & 0xf) << 4;
	writel(reg_val, reg_base + SCR_RPT_OFF);
}

static inline void scr_set_tx_repeat(void __iomem *reg_base, uint8_t repeat)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_RPT_OFF);
	reg_val &= ~(0xf << 0);
	reg_val |= (repeat & 0xf) << 0;
	writel(reg_val, reg_base + SCR_RPT_OFF);
}

/* baud = F_sysclk/(2*(BAUDDIV+1)), BAUDDIV=bit[31:16] */
static inline void scr_set_baud_divisor(void __iomem *reg_base,
		uint16_t divisor)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_DIV_OFF);
	reg_val &= ~(0xffffU << 16);
	reg_val |= divisor << 16;
	writel(reg_val, reg_base + SCR_DIV_OFF);
}

static inline uint16_t scr_get_baud_divisor(void __iomem *reg_base)
{
	return readl(reg_base + SCR_DIV_OFF) >> 16 & 0xffff;
}

/* F_scclk = F_sysclk/(2*(SCCLK+1)), SCCLK=bit[15:0] */
static inline void scr_set_scclk_divisor(void __iomem *reg_base,
		uint16_t divisor)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_DIV_OFF);
	reg_val &= ~0xffffU;
	reg_val |= divisor;
	writel(reg_val, reg_base + SCR_DIV_OFF);
}

static inline uint16_t scr_get_scclk_divisor(void __iomem *reg_base)
{
	return readl(reg_base + SCR_DIV_OFF) & 0xffff;
}

/* ATR start time limit, it define the maximum time of ATR response
 * limit_time = 128*ATR*T_scclk, ATR=bit[23:16], T_scclk=1/F_scclk
 */
static inline void scr_set_atr_time(void __iomem *reg_base, uint8_t scclk)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_LTIM_OFF);
	reg_val &= ~(0xff << 16);
	reg_val |= scclk << 16;
	writel(reg_val, reg_base + SCR_LTIM_OFF);
}

/* reset duration, dura = 128*RST*T_scclk, RST=bit[15:8], T_scclk=1/F_scclk */
static inline void scr_set_reset_time(void __iomem *reg_base, uint8_t scclk)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_LTIM_OFF);
	reg_val &= ~(0xff << 8);
	reg_val |= scclk << 8;
	writel(reg_val, reg_base + SCR_LTIM_OFF);
}

/* reset duration, dura = 128*ACT*T_scclk, ACT=bit[7:0], T_scclk=1/F_scclk */
static inline void scr_set_activation_time(void __iomem *reg_base,
		uint8_t scclk)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_LTIM_OFF);
	reg_val &= ~0xff;
	reg_val |= scclk;
	writel(reg_val, reg_base + SCR_LTIM_OFF);
}

static inline uint32_t scr_get_line_time(void __iomem *reg_base)
{
	return readl(reg_base + SCR_LTIM_OFF);
}

/* character limit, maximum time of two consecutive character, ETU as unit */
static inline void scr_set_chlimit_time(void __iomem *reg_base, uint16_t etu)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CTIM_OFF);
	reg_val &= ~(0xffffU << 16);
	reg_val |= etu << 16;
	writel(reg_val, reg_base + SCR_CTIM_OFF);
}

/* character guard time, delay time of each character, ETU as unit */
static inline void scr_set_guard_time(void __iomem *reg_base, uint8_t etu)
{
	uint32_t reg_val;

	reg_val = readl(reg_base + SCR_CTIM_OFF);
	reg_val &= ~0xff;
	reg_val |= etu;
	writel(reg_val, reg_base + SCR_CTIM_OFF);
}

static inline uint32_t scr_get_character_time(void __iomem *reg_base)
{
	return readl(reg_base + SCR_CTIM_OFF);
}

static inline uint32_t scr_get_fsm(void __iomem *reg_base)
{
	return readl(reg_base + SCR_FSM_OFF);
}

static inline void scr_write_fifo(void __iomem *reg_base, uint8_t data)
{
	writel(data, reg_base + SCR_FIFO_OFF);
}

static inline uint8_t scr_read_fifo(void __iomem *reg_base)
{
	return readl(reg_base + SCR_FIFO_OFF) & 0xff;
}

/* =========================  end  =================================== */

/* IRQ interrupt handler */
static irqreturn_t sunxi_scr_interrupt(int irqno, void *dev_id)
{
	struct sunxi_scr *pscr = (struct sunxi_scr *)dev_id;
	uint32_t rx_cnt = 0, i = 0;
	u32 irq_status = 0;

	irq_status = scr_get_interrupt_status(pscr->reg_base);
	scr_clear_interrupt_status(pscr->reg_base, irq_status);

	SCR_DBG("irq_status = 0x%08x\n", irq_status);
	if (irq_status & SCR_INTSTA_INS) {
		SCR_DBG("SmartCard Inserted!!\n");
		scr_set_activation(pscr->reg_base);
		pscr->card_in = true;
		/* avoid error multi trigger */
		mod_timer(&pscr->poll_timer, jiffies + HZ/100); /* 10ms */
	}

	if (irq_status & SCR_INTSTA_REM) {
		SCR_DBG("SmartCard Removed!!\n\n");
		scr_set_deactivation(pscr->reg_base);
		pscr->card_in = false;
		/* avoid error multi trigger */
		mod_timer(&pscr->poll_timer, jiffies + HZ/100); /* 10ms */
	}

	if (irq_status & SCR_INTSTA_ACT) {
		SCR_DBG("SmartCard Activated!!\n");
		memset(&scr_buf_rx, 0, sizeof(struct scr_data));
		pscr->card_in = true;
	}

	if (irq_status & SCR_INTSTA_DEACT) {
		SCR_DBG("SmartCard Deactivated!!\n");
		pscr->card_in = false;
	}

	if ((irq_status & SCR_INTSTA_RXDONE) ||
	    (irq_status & SCR_INTSTA_RXFTH) ||
	    (irq_status & SCR_INTSTA_RXFFULL)) {
		SCR_DBG("SmartCard Rx interrupt!!\n");
		rx_cnt = scr_get_rxfifo_count(pscr->reg_base);
		SCR_DBG("rx_cnt=%d\n", rx_cnt);
		if (rx_cnt > (SCR_BUF_SIZE - scr_buf_rx.cnt)) {
			SCR_ERR("There are not more space filled in RX buffer");
		} else {
			spin_lock(&pscr->rx_lock);
			for (i = 0; i < rx_cnt; i++) {
				scr_buf_rx.buf[scr_buf_rx.cnt] =
					scr_read_fifo(pscr->reg_base);
				scr_buf_rx.cnt++;
			}
			spin_unlock(&pscr->rx_lock);
		}
	}

	if (irq_status & SCR_INTSTA_RXPERR) {
		SCR_ERR("SmartCard Rx Parity Error!!\n");
	}

	if (irq_status & SCR_INTSTA_ATRFAIL) {
		SCR_DBG("SmartCard ATR Fail!!, again set ACT!\n");
		if (pscr->atr_resp == SCR_ATR_RESP_AGAIN)
			pscr->atr_resp = SCR_ATR_RESP_FAIL;
		else
			pscr->atr_resp = SCR_ATR_RESP_AGAIN;
		/* set activation again */
		scr_set_activation(pscr->reg_base);
	}

	if (irq_status & SCR_INTSTA_ATRDONE) {
		SCR_DBG("SmartCard ATR Done!!\n");
		memcpy(pscr->scr_atr_des.atr_data, scr_buf_rx.buf,
				scr_buf_rx.cnt);
		pscr->scr_atr_des.atr_len = scr_buf_rx.cnt;
		pscr->atr_resp = SCR_ATR_RESP_OK;
		/* parse ATR data to reconfig smart card */
		sunxi_scr_do_atr(pscr);
	}
	if (irq_status & SCR_INTSTA_CHTO) {
		SCR_DBG("character timeout!!\n");
		pscr->rx_transmit_status = SCR_RX_TRANSMIT_TMOUT;
	}

	if (irq_status & SCR_INTSTA_TXFEMPTY)
		SCR_DBG("SmartCard TX Empty!!\n");

	if (irq_status & SCR_INTSTA_TXDONE)
		SCR_DBG("SmartCard TX Done!!\n");

	if (irq_status & SCR_INTSTA_TXPERR)
		SCR_ERR("SmartCard TX Error!!\n");

	if (irq_status & SCR_INTSTA_TXFDONE)
		SCR_DBG("SmartCard TX FIFO Done!!\n");

	return IRQ_HANDLED;
}

static int scr_request_gpio(struct sunxi_scr *pscr)
{
	int ret = 0;
	struct pinctrl_state *pctrl_state = NULL;

	pscr->scr_pinctrl = devm_pinctrl_get(&(pscr->scr_device->dev));
	if (IS_ERR_OR_NULL(pscr->scr_pinctrl)) {
		SCR_ERR("request pinctrl handle fail!\n");
		return -EINVAL;
	}

	pctrl_state = pinctrl_lookup_state(pscr->scr_pinctrl,
						PINCTRL_STATE_DEFAULT);
	if (IS_ERR(pctrl_state)) {
		SCR_ERR("pinctrl_lookup_state fail! return %p\n", pctrl_state);
		return -EINVAL;
	}

	ret = pinctrl_select_state(pscr->scr_pinctrl, pctrl_state);
	if (ret < 0)
		SCR_ERR("pinctrl_select_state fail! return %d\n", ret);

	return ret;
}

static void scr_release_gpio(struct sunxi_scr *pscr)
{
	if (!IS_ERR_OR_NULL(pscr->scr_pinctrl))
		devm_pinctrl_put(pscr->scr_pinctrl);
	pscr->scr_pinctrl = NULL;
}

static uint32_t scr_init_reg(struct sunxi_scr *pscr)
{
	scr_global_interrupt_disable(pscr->reg_base);
	scr_set_interrupt_disable(pscr->reg_base, 0xffffffff);
	scr_clear_interrupt_status(pscr->reg_base, 0xffffffff);

	scr_flush_txfifo(pscr->reg_base);
	scr_flush_rxfifo(pscr->reg_base);

	scr_set_txfifo_threshold(pscr->reg_base, pscr->txfifo_thh);
	scr_set_rxfifo_threshold(pscr->reg_base, pscr->rxfifo_thh);

	scr_set_tx_repeat(pscr->reg_base, pscr->tx_repeat);
	scr_set_rx_repeat(pscr->reg_base, pscr->rx_repeat);

	scr_set_scclk_divisor(pscr->reg_base, pscr->scclk_div);
	scr_set_baud_divisor(pscr->reg_base, pscr->baud_div);
	scr_set_activation_time(pscr->reg_base, pscr->act_time);
	scr_set_reset_time(pscr->reg_base, pscr->rst_time);
	scr_set_atr_time(pscr->reg_base, pscr->atr_time);
	scr_set_guard_time(pscr->reg_base, pscr->guard_time);
	scr_set_chlimit_time(pscr->reg_base, pscr->chlimit_time);
	scr_set_atr_flush(pscr->reg_base, 1);
	scr_set_ts_recv(pscr->reg_base, 1);
	scr_set_t_protocol(pscr->reg_base, pscr->card_para.protocol_type);

	scr_receive_enable(pscr->reg_base);
	scr_transmit_enable(pscr->reg_base);

	scr_set_interrupt_enable(pscr->reg_base, pscr->inten_bm);
	scr_global_interrupt_enable(pscr->reg_base);

	scr_set_recv_parity(pscr->reg_base,
			    pscr->card_para.recv_no_parity);

	return 0;
}

static void sunxi_scr_param_init(struct sunxi_scr *pscr)
{
	/* init register parameters */
	pscr->inten_bm = 0xfffffff0;
	pscr->txfifo_thh = SCR_FIFO_DEPTH;
	pscr->rxfifo_thh = SCR_FIFO_DEPTH;
	pscr->tx_repeat = 0x3;
	pscr->rx_repeat = 0x3;
	/* (APB1CLK/4000000) PCLK/14, <175, && SCCLK >= 1M && =<4M */
	pscr->scclk_div = 0;
	pscr->baud_div = 0;	/* ETU = 372*SCCLK */
	pscr->act_time = 2;	/* =1*256, 100 */
	pscr->rst_time = 0xff;	/* 2*256, >=400 */
	/* scr.atr_time = (40000>>8)+1; //=256*256, 400~40000 */
	pscr->atr_time = 0xff;
	pscr->guard_time = 2;	/* =2*ETUs */
	/* interval time (400-1) characters */
	pscr->chlimit_time = 100 * (10 + pscr->guard_time);
	pscr->atr_resp = SCR_ATR_RESP_INVALID;
	pscr->rx_transmit_status = SCR_RX_TRANSMIT_NOYET;

	/* init card parameters */
	pscr->card_para.f = 372;
	pscr->card_para.d = 1;
	/* 3.579MHz, unit is KHz, expect baud=9600bps*/
	pscr->card_para.freq = 3579;
	pscr->card_para.recv_no_parity = 1;
	pscr->card_para.protocol_type = 0;

	/* init atr data */
	pscr->smc_atr_para.TS = 0x3B;
	pscr->smc_atr_para.TK_NUM = 0x00;

	pscr->smc_atr_para.T = 0;	/* T=0 Protocol */
	pscr->smc_atr_para.FMAX = 4;	/* 4MHz, unit is MHz */
	pscr->smc_atr_para.F = 372;
	pscr->smc_atr_para.D = 1;
	pscr->smc_atr_para.I = 50;	/* 50mA */
	pscr->smc_atr_para.P = 5;	/* 5V */
	pscr->smc_atr_para.N = 2;

	/* pscr->clk_freq'unit is hz but pscr->card_para.freq'unit is khz */
	pscr->scclk_div = pscr->clk_freq / pscr->card_para.freq / 2000 - 1;
	pscr->baud_div = (pscr->scclk_div + 1) *
			 (pscr->card_para.f / pscr->card_para.d) - 1;

	SCR_DBG("clk_freq=%d, scclk_div=%d, baud_div=%d\n",
		pscr->clk_freq, pscr->scclk_div, pscr->baud_div);

	/* init registers */
	scr_init_reg(pscr);
}

/* use ATR data to reconfig smart card control register */
static void sunxi_scr_do_atr(struct sunxi_scr *pscr)
{
	struct scr_atr *pscr_atr_des = &pscr->scr_atr_des;
	struct smc_atr_para *psmc_atr_para = &pscr->smc_atr_para;
	struct smc_pps_para *psmc_pps_para = &pscr->smc_pps_para;

	SCR_DBG("\nBefore Decode:\n"
		"psmc_atr_para->TS   = 0x%x\n"
		"psmc_atr_para->T    = %d\n"
		"psmc_atr_para->FMAX = %d(MHz), Current: %d(KHz)\n"
		"psmc_atr_para->F    = %d\n"
		"psmc_atr_para->D    = %d\n",
		psmc_atr_para->TS,
		psmc_atr_para->T,
		psmc_atr_para->FMAX,
		pscr->card_para.freq, psmc_atr_para->F, psmc_atr_para->D);

	smartcard_atr_decode(psmc_atr_para, psmc_pps_para,
			     (uint8_t *)&pscr_atr_des->atr_data, 1);


	SCR_DBG("\nAfter Decode:\n"
		"psmc_atr_para->TS   = 0x%x\n"
		"psmc_atr_para->T    = %d\n"
		"psmc_atr_para->FMAX = %d(MHz), Current: %d(KHz)\n"
		"psmc_atr_para->F    = %d\n"
		"psmc_atr_para->D    = %d\n",
		psmc_atr_para->TS,
		psmc_atr_para->T,
		psmc_atr_para->FMAX,
		pscr->card_para.freq, psmc_atr_para->F, psmc_atr_para->D);


	/* use default F&D or set by up layer
	   pscr->card_para.f = pscr->smc_atr_para.F;
	   pscr->card_para.d = pscr->smc_atr_para.D;
	   pscr->card_para.freq = psmc_atr_para->FMAX * 1000;

	   pscr->scclk_div = pscr->clk_freq/pscr->card_para.freq/2000-1;
	   pscr->baud_div = (pscr->scclk_div + 1) *
	   (pscr->card_para.f/pscr->card_para.d)-1;

	   scr_set_scclk_divisor(pscr->reg_base, pscr->scclk_div);
	   scr_set_baud_divisor(pscr->reg_base, pscr->baud_div);
	 */
	pscr->card_para.protocol_type = psmc_atr_para->T;
	scr_set_t_protocol(pscr->reg_base, pscr->card_para.protocol_type);

	if (0x3f == psmc_atr_para->TS) {
		/* 0x3b:direct convention, 0x3f:inverse convention */
		scr_set_data_order(pscr->reg_base, 1);
		scr_set_data_invert(pscr->reg_base, 1);
	}
}

static uint32_t sunxi_scr_clk_init(struct sunxi_scr *pscr)
{
	struct platform_device *pdev = pscr->scr_device;
	struct device_node *node = pdev->dev.of_node;

	if (NULL == pdev || !of_device_is_available(node)) {
		SCR_ERR("platform_device invalid!\n");
		return -EINVAL;
	}

	pscr->scr_clk = of_clk_get(node, 0);
	if (!pscr->scr_clk || IS_ERR(pscr->scr_clk)) {
		SCR_ERR("try to get scr clock fail!\n");
		return -EINVAL;
	}

	pscr->scr_clk_source = of_clk_get(node, 1);
	if (!pscr->scr_clk_source || IS_ERR(pscr->scr_clk_source)) {
		SCR_ERR("err: try to get scr_clk_source clock fail!\n");
		return -EINVAL;
	}

	if (clk_set_parent(pscr->scr_clk, pscr->scr_clk_source)) {
		SCR_ERR("set scr_clk parent to scr_clk_source fail!\n");
		return -EINVAL;
	}

	if (of_property_read_u32(node, "clock-frequency", &pscr->clk_freq)) {
		SCR_INFO("get clock-frequency fail! use default 24Mhz\n");
		pscr->clk_freq = 24000000;
	}

	if (clk_set_rate(pscr->scr_clk, pscr->clk_freq)) {
		SCR_ERR("set ir scr_clk freq  failed!\n");
		return -EINVAL;
	}

	if (clk_prepare_enable(pscr->scr_clk)) {
		SCR_ERR("try to enable scr_clk failed!\n");
		return -EINVAL;
	}

	return 0;
}

static uint32_t sunxi_scr_clk_exit(struct sunxi_scr *pscr)
{
	if (NULL == pscr->scr_clk || IS_ERR(pscr->scr_clk)) {
		SCR_ERR("scr_clk handle is invalid, just return!\n");
		return -EINVAL;
	} else {
		clk_disable_unprepare(pscr->scr_clk);
		clk_put(pscr->scr_clk);
		pscr->scr_clk = NULL;
	}
	if (NULL == pscr->scr_clk_source || IS_ERR(pscr->scr_clk_source)) {
		SCR_ERR("scr_clk_source handle is invalid, just return!\n");
		return -EINVAL;
	} else {
		clk_put(pscr->scr_clk_source);
		pscr->scr_clk_source = NULL;
	}

	return 0;
}

static int sunxi_scr_open(struct inode *inode, struct file *file)
{
	file->private_data = pscr;

	if (pscr->open_cnt > 0) {
		SCR_DBG("smart card opened already\n");
		pscr->open_cnt++;
		return 0;
	}

	sunxi_scr_param_init(pscr);
	pscr->card_in = scr_get_det_status(pscr->reg_base) ? true : false;
	pscr->card_last = pscr->card_in;
	if (true == pscr->card_in)
		scr_set_activation(pscr->reg_base);

	pscr->open_cnt++;

	return 0;
}

static int sunxi_scr_release(struct inode *inode, struct file *file)
{
	struct sunxi_scr *pscr = file->private_data;

	if (--pscr->open_cnt) {
		SCR_DBG("There is not really close, just return!\n");
		return 0;
	}

	scr_set_deactivation(pscr->reg_base);
	scr_clear_csr_reg(pscr->reg_base);

	return 0;
}

static ssize_t
sunxi_scr_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	struct sunxi_scr *pscr = file->private_data;
	uint32_t rx_size = 0;
	int try_num = 100;

	while ((SCR_RX_TRANSMIT_NOYET == pscr->rx_transmit_status) && try_num--)
		msleep(50);

	if (try_num < 0) {
		SCR_ERR("read timeout\n");
		return -EAGAIN;
	}

	rx_size = scr_buf_rx.cnt;
	if (rx_size > size)
		rx_size = size;

	if (copy_to_user(buf, scr_buf_rx.buf, rx_size))
		return -EFAULT;
	scr_flush_rxfifo(pscr->reg_base);

	return rx_size;
}

static int scr_write(struct sunxi_scr *pscr, char *buf, int size)
{
	int try_num = 100;
	int i;

	for (i = 0; i < size; i++) {
		while (scr_txfifo_is_full(pscr->reg_base) && try_num--)
			msleep(50);
		if (try_num < 0) {
			SCR_ERR("TX FIFO full, write timeout\n");
			break;
		}
		scr_write_fifo(pscr->reg_base, buf[i]);
		try_num = 100;
	}
	SCR_DBG("writed %d byte\n", i);

	return i;
}

static ssize_t sunxi_scr_write(struct file *file, const char __user *buf,
						size_t size, loff_t *ppos)
{
	struct sunxi_scr *pscr = file->private_data;

	if (copy_from_user(scr_buf_tx.buf, buf, size))
		return -EFAULT;

	scr_flush_txfifo(pscr->reg_base);
	memset(&scr_buf_rx, 0, sizeof(struct scr_data));
	pscr->rx_transmit_status = SCR_RX_TRANSMIT_NOYET;

	return scr_write(pscr, scr_buf_tx.buf, size);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
static void scr_timer_handler(struct timer_list *list)
{
	wake_up(&pscr->scr_poll);
}
#else
static void scr_timer_handler(unsigned long data)
{
	(void)data;
	wake_up(&pscr->scr_poll);
}
#endif

unsigned int sunxi_scr_poll(struct file *file, struct poll_table_struct *wait)
{
	struct sunxi_scr *pscr = file->private_data;
	unsigned int mask = 0;

	/* add wait_queue to poll_table */
	poll_wait(file, &pscr->scr_poll,  wait);

	/* using Edge Triggered instand of Level Triggered */
	if (pscr->card_last^pscr->card_in) {
		if (pscr->card_in)
			mask |= POLLIN;
		else
			mask |= POLLOUT;

		pscr->card_last = pscr->card_in;
	}

	return mask;
}

static long sunxi_scr_ioctl(struct file *file, uint32_t cmd, unsigned long arg)
{
	struct sunxi_scr *pscr = file->private_data;
	uint32_t tmp = 0, ret = 0;
	int try_num = 300;

	SCR_ENTER();

	switch (cmd) {
	/* get smart card status 0:SCR_CARD_OUT, 1:SCR_CARD_IN */
	case SCR_IOCGSTATUS:
		tmp = scr_get_det_status(pscr->reg_base);
		ret = put_user(tmp, (int __user *)arg);
		break;

	/* reset card and store ATR data immediately */
	case SCR_IOCRESET:
		scr_set_activation(pscr->reg_base);
		mdelay(10);
		break;

	/* get ATR data, the arg type is struct scr_atr */
	case SCR_IOCGATR:
		while (SCR_ATR_RESP_OK != pscr->atr_resp && try_num--) {
			msleep(10);
			if ((pscr->rx_transmit_status == SCR_RX_TRANSMIT_TMOUT)
				&& (pscr->atr_resp == SCR_ATR_RESP_AGAIN)
				&& (scr_buf_rx.cnt > 0)) {
				memcpy(pscr->scr_atr_des.atr_data,
					scr_buf_rx.buf, scr_buf_rx.cnt);
				pscr->scr_atr_des.atr_len = scr_buf_rx.cnt;
				pscr->atr_resp = SCR_ATR_RESP_OK;
				/* parse ATR data to reconfig smart card */
				sunxi_scr_do_atr(pscr);
				break;
			}

		};
		if (try_num < 0) {
			SCR_ERR("SCR_IOCGATR timeout!\n");
			ret = -EAGAIN;
			break;
		}
		ret = copy_to_user((void __user *)arg, &pscr->scr_atr_des,
				   sizeof(struct scr_atr)) ? -EFAULT : 0;
		break;

	/* get current card parameters & status */
	case SCR_IOCGPARA:
		ret = copy_to_user((void __user *)arg, &pscr->card_para,
				   sizeof(struct scr_card_para)) ? -EFAULT : 0;
		break;

	/* set current card parameters & status */
	case SCR_IOCSPARA:
		if (copy_from_user(&pscr->card_para, (void __user *)arg,
				   sizeof(struct scr_card_para))) {
			SCR_ERR("get card para from user error!\n");
			ret = -EFAULT;
			break;
		}
		pscr->scclk_div = pscr->clk_freq / pscr->card_para.freq / 2000
			- 1;
		pscr->baud_div = (pscr->scclk_div + 1) *
			(pscr->card_para.f / pscr->card_para.d) - 1;
		scr_set_scclk_divisor(pscr->reg_base, pscr->scclk_div);
		scr_set_baud_divisor(pscr->reg_base, pscr->baud_div);
		scr_set_recv_parity(pscr->reg_base,
				pscr->card_para.recv_no_parity);
		scr_set_t_protocol(pscr->reg_base,
				pscr->card_para.protocol_type);
		break;

	/* get the parse parameters come from ATR data */
	case SCR_IOCGATRPARA:
		ret = copy_to_user((void __user *)arg, &pscr->smc_atr_para,
				   sizeof(struct smc_atr_para)) ? -EFAULT : 0;
		break;

	/* get the pps parse parameters come from ATR data */
	case SCR_IOCGPPSPARA:
		ret = copy_to_user((void __user *)arg, &pscr->smc_pps_para,
				   sizeof(struct smc_pps_para)) ? -EFAULT : 0;
		break;

	/* write cmd and read data immediately */
	case SCR_IOCWRDATA: {
		int rtn_data_len;
		struct scr_wr_data wr_data;

		if (copy_from_user(&wr_data, (void __user *)arg,
				   sizeof(struct scr_wr_data))) {
			SCR_ERR("get wr_data from user error!\n");
			ret = -EFAULT;
			break;
		}
		if (copy_from_user(scr_buf_tx.buf,
				  (void __user *)wr_data.cmd_buf,
				  wr_data.cmd_len)) {
			SCR_ERR("get wr_data cmd_buf from user error!\n");
			ret = -EFAULT;
			break;
		}
		scr_buf_tx.cnt = wr_data.cmd_len;

		scr_flush_txfifo(pscr->reg_base);
		scr_flush_rxfifo(pscr->reg_base);
		memset(&scr_buf_rx, 0, sizeof(struct scr_data));

		/* APDU, smart card cammand format */
		/* type1: CLS + INS + P1 + P2 + le  -> only read, le=read size*/
		if (5 == scr_buf_tx.cnt) {
			scr_write(pscr, scr_buf_tx.buf, 5);
			/* respond:
			 * INS(=buf[1]) + valid_data(=buf[4]) + SW1 + SW2
			 */
			rtn_data_len = scr_buf_tx.buf[4] + 3;
			while ((rtn_data_len > scr_buf_rx.cnt) && try_num--) {
				msleep(10);
			};

			if (try_num < 0) {
				SCR_ERR("read timeout\n");
				ret = -EFAULT;
				break;
			}
			wr_data.rtn_len = scr_buf_tx.buf[4];
			wr_data.psw1 = scr_buf_rx.buf[rtn_data_len - 2];
			wr_data.psw2 = scr_buf_rx.buf[rtn_data_len - 1];
			ret = copy_to_user((void __user *)arg,
				&wr_data,
				sizeof(struct scr_wr_data)) ? -EFAULT : 0;
			if (ret != 0)
				return ret;
			ret = copy_to_user((void __user *)wr_data.rtn_data,
				&scr_buf_rx.buf[1],
				scr_buf_tx.buf[4]) ? -EFAULT : 0;

		/* type2: CLS + INS + P1 + P2 + lc + data
		 * only lc, write data, lc=data size
		 */
		} else if (scr_buf_tx.buf[4]+5 == scr_buf_tx.cnt) {
			scr_write(pscr, scr_buf_tx.buf, 5);
			while ((0 == scr_buf_rx.cnt) && try_num--) {
				msleep(10);
			};
			if (try_num < 0) {
				SCR_ERR("timeout: there is not any data\n");
				ret = -EFAULT;
				break;
			}
			if (scr_buf_rx.buf[0] != scr_buf_tx.buf[1]) {
				SCR_ERR("do not support this instruction\n");
				ret = -EFAULT;
				break;
			}
			scr_write(pscr, &scr_buf_tx.buf[5], scr_buf_tx.buf[4]);
			try_num = 300;
			while ((scr_buf_rx.cnt < 3) && try_num--) {
				msleep(10);
			};
			if (try_num < 0) {
				SCR_ERR("timeout: get sw1,sw2 fail\n");
				ret = -EFAULT;
				break;
			}
			wr_data.psw1 = scr_buf_rx.buf[1];
			wr_data.psw2 = scr_buf_rx.buf[2];
			ret = copy_to_user((void __user *)arg,
				&wr_data,
				sizeof(struct scr_wr_data)) ? -EFAULT : 0;

		/* type3: CLS + INS + P1 + P2 + lc + data +le -> le+lc  */
		} else if (scr_buf_tx.buf[4]+6 == scr_buf_tx.cnt) {
			scr_write(pscr, scr_buf_tx.buf, 5);
			while ((0 == scr_buf_rx.cnt) && try_num--) {
				msleep(10);
			};
			if (try_num < 0) {
				SCR_ERR("timeout: there is not any data\n");
				ret = -EFAULT;
				break;
			}
			if (scr_buf_rx.buf[0] != scr_buf_tx.buf[1]) {
				SCR_ERR("do not support this instruction\n");
				ret = -EFAULT;
				break;
			}
			scr_write(pscr, &scr_buf_tx.buf[5],
					scr_buf_tx.buf[4]+1);
			try_num = 300;
			/* respond: INS + valid_data + SW1 + SW2 */
			rtn_data_len = scr_buf_tx.buf[scr_buf_tx.cnt-1] + 3;
			while ((rtn_data_len > scr_buf_rx.cnt) && try_num--) {
				msleep(10);
			};

			if (try_num < 0) {
				SCR_ERR("read timeout\n");
				ret = -EFAULT;
				break;
			}

			wr_data.rtn_len = scr_buf_tx.buf[scr_buf_tx.cnt-1];
			wr_data.psw1 = scr_buf_rx.buf[rtn_data_len - 2];
			wr_data.psw2 = scr_buf_rx.buf[rtn_data_len - 1];
			ret = copy_to_user((void __user *)arg,
				&wr_data,
				sizeof(struct scr_wr_data)) ? -EFAULT : 0;
			if (ret != 0)
				return ret;
			ret = copy_to_user((void __user *)wr_data.rtn_data,
				&scr_buf_rx.buf[1],
				scr_buf_tx.buf[scr_buf_tx.cnt-1]) ? -EFAULT : 0;

		} else {
			SCR_ERR("invalid command format\n");
			ret = -EFAULT;
			break;
		}
		break;
		}

	default:
		SCR_ERR("Invalid iocontrol command!\n");
		break;
	}

	return ret;
}

static const struct file_operations sunxi_scr_fops = {
	.owner = THIS_MODULE,
	.llseek = noop_llseek,
	.read = sunxi_scr_read,
	.write = sunxi_scr_write,
	.unlocked_ioctl = sunxi_scr_ioctl,
	.open = sunxi_scr_open,
	.release = sunxi_scr_release,
	.poll = sunxi_scr_poll,
};

static int sunxi_scr_probe(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	struct resource *mem_res = NULL;
	int ret = 0;

	SCR_ENTER();

	pscr = kzalloc(sizeof(struct sunxi_scr), GFP_KERNEL);
	if (!pscr) {
		SCR_ERR("kzalloc struct sunxi_scr fail!\n");
		return -ENOMEM;
	}

	pscr->scr_device = pdev;
	if (!of_device_is_available(node)) {
		SCR_ERR("invalid node!\n");
		ret = -EINVAL;
		goto emloc;
	}

	if (sunxi_scr_clk_init(pscr)) {
		SCR_ERR("sunxi_scr_clk_init fail!\n");
		ret = -EINVAL;
		goto eclk;
	}

	pscr->irq_no = platform_get_irq(pdev, 0);
	if (pscr->irq_no < 0) {
		SCR_ERR("get irq number fail!\n");
		ret = -EINVAL;
		goto eclk;
	}

	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (mem_res == NULL) {
		SCR_ERR("failed to get MEM res\n");
		ret = -ENXIO;
		goto eclk;
	}

	if (!request_mem_region(mem_res->start,
		resource_size(mem_res), mem_res->name)) {
		SCR_ERR("failed to request mem region\n");
		ret = -EINVAL;
		goto eclk;
	}

	pscr->reg_base = ioremap(mem_res->start, resource_size(mem_res));
	if (!pscr->reg_base) {
		SCR_ERR("failed to io remap\n");
		ret = -EIO;
		goto eiomem;
	}
	pscr->mem_res = mem_res;

	if (request_irq(pscr->irq_no, sunxi_scr_interrupt,
			IRQF_TRIGGER_NONE, "scr", pscr)) {
		SCR_ERR("request irq fail!\n");
		ret = -EINVAL;
		goto eiomap;
	}

	if (scr_request_gpio(pscr)) {
		SCR_ERR("failed to request gpio\n");
		ret = -EINVAL;
		goto eirq;
	}

	spin_lock_init(&pscr->rx_lock);
	sunxi_scr_param_init(pscr);
	init_waitqueue_head(&pscr->scr_poll);
	pscr->poll_timer.expires = jiffies + HZ/100; /* 10ms */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
	timer_setup(&pscr->poll_timer, scr_timer_handler, 0);
#else
	pscr->poll_timer.function = scr_timer_handler;
	init_timer(&pscr->poll_timer);
	add_timer(&pscr->poll_timer);
#endif
	/* creat character device */
	sunxi_scr_major = register_chrdev(0, SCR_MODULE_NAME, &sunxi_scr_fops);
	if (sunxi_scr_major < 0) {
		SCR_ERR("register_chrdev fail!\n");
		ret = -ENODEV;
		goto eirq;
	}
	scr_dev_class = class_create(THIS_MODULE, SCR_MODULE_NAME);
	if (IS_ERR(scr_dev_class)) {
		SCR_ERR("class_create fail!\n");
		ret = -ENODEV;
		goto edev;
	}
	scr_device = device_create(scr_dev_class, NULL,
			MKDEV(sunxi_scr_major, 0),
				   NULL, SCR_MODULE_NAME);
	if (IS_ERR(scr_device)) {
		SCR_ERR("device_create fail!\n");
		ret = -ENODEV;
		goto ecla;
	}

	platform_set_drvdata(pdev, pscr);

	return 0;

ecla:
	class_destroy(scr_dev_class);
edev:
	unregister_chrdev(sunxi_scr_major, SCR_MODULE_NAME);
eirq:
	free_irq(pscr->irq_no, pscr);
eiomap:
	iounmap(pscr->reg_base);
eiomem:
	release_mem_region(mem_res->start, resource_size(mem_res));
eclk:
	sunxi_scr_clk_exit(pscr);
emloc:
	kfree(pscr);
	return ret;

}

static int sunxi_scr_remove(struct platform_device *pdev)
{
	struct sunxi_scr *pscr = platform_get_drvdata(pdev);

	device_destroy(scr_dev_class, MKDEV(sunxi_scr_major, 0));
	class_destroy(scr_dev_class);
	unregister_chrdev(sunxi_scr_major, SCR_MODULE_NAME);
	free_irq(pscr->irq_no, pscr);
	iounmap(pscr->reg_base);
	release_mem_region(pscr->mem_res->start,
			   resource_size(pscr->mem_res));
	scr_release_gpio(pscr);
	sunxi_scr_clk_exit(pscr);

	del_timer(&pscr->poll_timer);

	SCR_EXIT();

	return 0;
}

#ifdef CONFIG_PM
static int sunxi_scr_suspend(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct sunxi_scr *pscr = platform_get_drvdata(pdev);
	struct pinctrl_state *pctrl_state = NULL;

	pscr->suspended = true;

	if (sunxi_scr_clk_exit(pscr)) {
		SCR_ERR("SCR suspend failed !\n");
		pscr->suspended = false;
		return -1;
	}

	if (!IS_ERR_OR_NULL(pscr->scr_pinctrl)) {
		pctrl_state = pinctrl_lookup_state(pscr->scr_pinctrl,
							PINCTRL_STATE_SLEEP);
		if (IS_ERR(pctrl_state)) {
			SCR_ERR("SCR pinctrl lookup sleep fail\n");
			return -1;
		}

		if (pinctrl_select_state(pscr->scr_pinctrl, pctrl_state) < 0) {
			SCR_ERR("SCR pinctrl select sleep fail\n");
			return -1;
		}
	}

	disable_irq_nosync(pscr->irq_no);

	SCR_DBG("SCR suspend okay\n");
	return 0;
}

static int sunxi_scr_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct sunxi_scr *pscr = platform_get_drvdata(pdev);
	struct pinctrl_state *pctrl_state = NULL;


	pscr->suspended = false;

	if (sunxi_scr_clk_init(pscr)) {
		SCR_ERR("SCR resume failed !\n");
		return -1;
	}

	if (!IS_ERR_OR_NULL(pscr->scr_pinctrl)) {
		pctrl_state = pinctrl_lookup_state(pscr->scr_pinctrl,
							PINCTRL_STATE_DEFAULT);
		if (IS_ERR(pctrl_state)) {
			SCR_ERR("SCR pinctrl lookup default fail\n");
			return -1;
		}

		if (pinctrl_select_state(pscr->scr_pinctrl, pctrl_state) < 0) {
			SCR_ERR("SCR pinctrl select default fail\n");
			return -1;
		}
	}

	enable_irq(pscr->irq_no);

	SCR_DBG("SCR resume okay\n");
	return 0;
}

static const struct dev_pm_ops sunxi_scr_dev_pm_ops = {
	.suspend = sunxi_scr_suspend,
	.resume = sunxi_scr_resume,
};

#define SUNXI_SCR_DEV_PM_OPS (&sunxi_scr_dev_pm_ops)
#else
#define SUNXI_SCR_DEV_PM_OPS NULL
#endif

static const struct of_device_id sunxi_scr_match[] = {
	{.compatible = "allwinner,sunxi-scr",},
	{},
};

MODULE_DEVICE_TABLE(of, sunxi_scr_match);

static struct platform_driver scr_platform_driver = {
	.probe = sunxi_scr_probe,
	.remove = sunxi_scr_remove,
	.driver = {
		   .name = SCR_MODULE_NAME,
		   .owner = THIS_MODULE,
		   .pm = SUNXI_SCR_DEV_PM_OPS,
		   .of_match_table = sunxi_scr_match,
		   },
};

static int __init sunxi_scr_init(void)
{
	return platform_driver_register(&scr_platform_driver);
}

static void __exit sunxi_scr_exit(void)
{
	platform_driver_unregister(&scr_platform_driver);
}

module_init(sunxi_scr_init);
module_exit(sunxi_scr_exit);
MODULE_DESCRIPTION("Smart Card Driver");
MODULE_AUTHOR("fuzhaoke");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.1.0");
